package com.zf.poi.excel.utils;

import com.zf.poi.excel.annotations.ExcelHeader;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.Color;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 导出工具
 */
@Slf4j
public class ExcelWriteUtils {

    /**
     * Web导出,只适用于单行表头
     *
     * @param response   响应
     * @param fileName   文件名
     * @param excelClass 数据类型
     * @param dataList   数据
     */
    public static <T> void exportExcel(HttpServletResponse response, String fileName, Class<T> excelClass, List<T> dataList) {
        try {
            List<ExcelHeader> excelHeadList = getHeadList(excelClass);
            List<Map<String, Object>> excelDataList = getDataMap(dataList, excelClass);
            XSSFWorkbook workbook = new XSSFWorkbook();
            XSSFSheet sheet = workbook.createSheet("sheet1");
            // 记录每列的最大列宽
            HashMap<Integer, Integer> maxColumnWidthMap = new HashMap<>();
            // 样式
            CellStyle headStyle = createHeadStyle(workbook, new Color(10, 201, 34));
            CellStyle dataStyle = createDataStyle(workbook, null);

            // 写表头
            XSSFRow row = sheet.createRow(0);
            for (ExcelHeader excelHeader : excelHeadList) {
                XSSFCell cell = row.createCell(excelHeader.index());
                // 给单元格设置值
                cell.setCellValue(excelHeader.value());
                // 设置样式
                cell.setCellStyle(headStyle);
            }

            // 写数据
            int startRowNum = sheet.getPhysicalNumberOfRows();
            for (int i = startRowNum; i < excelDataList.size() + startRowNum; i++) {
                Map<String, Object> map = excelDataList.get(i - startRowNum);
                row = sheet.createRow(i);
                for (ExcelHeader excelHeader : excelHeadList) {
                    int columnIndex = excelHeader.index();
                    XSSFCell cell = row.createCell(columnIndex);
                    Object dataValue = map.get(excelHeader.value());
                    // 给单元格设置值
                    setCellValue(cell, dataValue);
                    // 设置样式
                    cell.setCellStyle(dataStyle);

                    // 获取设置值之后的列宽
                    int length = CellReadUtils.getCellDataStr(cell, null).getBytes().length * 256 + 512;
                    //这里把宽度最大限制到15000
                    if (length > 15000) {
                        length = 15000;
                    }
                    // 存储最大的列宽
                    Integer tm = maxColumnWidthMap.getOrDefault(columnIndex, 0);
                    maxColumnWidthMap.put(columnIndex, Math.max(length, tm));
                }
            }

            // 设置列宽
            for (Map.Entry<Integer, Integer> entry : maxColumnWidthMap.entrySet()) {
                sheet.setColumnWidth(entry.getKey(), entry.getValue());
            }
            ExcelUtils.decorateResponse(response, fileName);
            ServletOutputStream outputStream = response.getOutputStream();
            // 写入数据到响应
            workbook.write(outputStream);
            outputStream.flush();
            outputStream.close();
            workbook.close();
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 获取 表头
     *
     * @param excelClass 类的属性含有 ExcelHeader 注解的类
     * @return 表头
     */
    public static <T> List<ExcelHeader> getHeadList(Class<T> excelClass) {
        return Arrays.stream(excelClass.getDeclaredFields())
                .filter(item -> item.isAnnotationPresent(ExcelHeader.class))
                .map(item -> item.getAnnotation(ExcelHeader.class))
                .collect(Collectors.toList());
    }

    /**
     * 获取数据对象
     *
     * @param dataList   数据
     * @param excelClass 类的属性含有 ExcelHeader 注解的类
     * @return 表头的名称->值
     */
    public static <T> List<Map<String, Object>> getDataMap(List<T> dataList, Class<T> excelClass) throws IllegalAccessException {
        List<Map<String, Object>> res = new ArrayList<>();
        if (CollectionUtils.isEmpty(dataList)) {
            return res;
        }
        // 字段 -> 表头名
        Map<Field, String> fieldMap = Arrays.stream(excelClass.getDeclaredFields())
                .filter(item -> item.isAnnotationPresent(ExcelHeader.class))
                .collect(Collectors.toMap(item -> item, item -> item.getAnnotation(ExcelHeader.class).value()));
        Set<Field> fieldSet = fieldMap.keySet();
        for (Field field : fieldSet) {
            field.setAccessible(true);
        }

        // 将数据转为: 表头的名称->值
        for (T data : dataList) {
            HashMap<String, Object> tmp = new HashMap<>();
            for (Field field : fieldSet) {
                tmp.put(fieldMap.get(field), field.get(data));
            }
            res.add(tmp);
        }
        return res;
    }


    /**
     * 给单元格设置值
     * @param cell 单元格
     * @param object 值
     */
    public static void setCellValue(Cell cell, Object object) {
        if (object == null) {
            return;
        }
        if (Date.class.equals(object.getClass())) {
            cell.setCellValue((Date) object);
        } else if (LocalDateTime.class.equals(object.getClass())) {
            cell.setCellValue((LocalDateTime) object);
        } else if (LocalDate.class.equals(object.getClass())) {
            cell.setCellValue((LocalDate) object);
        } else if (Integer.class.equals(object.getClass())) {
            cell.setCellValue(((Integer) object).doubleValue());
        } else if (Double.class.equals(object.getClass())) {
            cell.setCellValue((Double) object);
        } else if (BigDecimal.class.equals(object.getClass())) {
            cell.setCellValue(((BigDecimal) object).toPlainString());
        } else if (BigInteger.class.equals(object.getClass())) {
            cell.setCellValue(((BigInteger) object).toString());
        } else if (String.class.equals(object.getClass())) {
            cell.setCellValue((String) object);
        }
    }


    /**
     * 创建样式
     * @param workbook excel
     * @param color 颜色
     */
    public static CellStyle createHeadStyle(XSSFWorkbook workbook, Color color) {
        XSSFCellStyle cellStyle = workbook.createCellStyle();
        // 边框
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);
        cellStyle.setBorderTop(BorderStyle.THIN);
        // 居中
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

        // 颜色
        if (color != null) {
            XSSFColor fgColor = new XSSFColor(color, new DefaultIndexedColorMap());
            cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
            cellStyle.setFillForegroundColor(fgColor);
        }
        return cellStyle;
    }

    /**
     * 创建样式
     * @param workbook excel
     * @param color 颜色
     */
    public static CellStyle createDataStyle(XSSFWorkbook workbook, Color color) {
        XSSFCellStyle cellStyle = workbook.createCellStyle();
        // 居中
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

        // 颜色
        if (color != null) {
            XSSFColor fgColor = new XSSFColor(color, new DefaultIndexedColorMap());
            cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
            cellStyle.setFillForegroundColor(fgColor);
        }
        return cellStyle;
    }

}
